/* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package limitedwip.watchdog.components; import com.intellij.ide.DataManager; import com.intellij.notification.Notification; import com.intellij.notification.NotificationListener; import com.intellij.notification.NotificationType; import com.intellij.notification.Notifications; import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.components.ApplicationComponent; import com.intellij.openapi.project.Project; import com.intellij.openapi.vcs.actions.CommonCheckinProjectAction; import com.intellij.openapi.vcs.changes.Change; import limitedwip.common.PluginId; import limitedwip.common.settings.LimitedWIPSettings; import limitedwip.common.settings.LimitedWipConfigurable; import org.jetbrains.annotations.NotNull; import javax.swing.event.HyperlinkEvent; import java.util.List; import static limitedwip.watchdog.components.VcsIdeUtil.registerBeforeCheckInListener; public class DisableLargeCommitsAppComponent implements ApplicationComponent, LimitedWipConfigurable.Listener { private static final int maxShowCommitDialogAttempts = 3; private boolean enabled; private int maxChangeSizeInLines; private boolean allowCommitOnceWithoutCheck = false; @Override public void initComponent() { registerBeforeCheckInListener(new VcsIdeUtil.CheckinListener() { @Override public boolean allowCheckIn(@NotNull Project project, @NotNull List<Change> changes) { if (allowCommitOnceWithoutCheck) { allowCommitOnceWithoutCheck = false; return true; } if (!enabled) return true; WatchdogComponent watchdogComponent = project.getComponent(WatchdogComponent.class); if (watchdogComponent == null) return true; int changeSize = watchdogComponent.currentChangeListSize(); if (changeSize > maxChangeSizeInLines) { notifyThatCommitWasCancelled(project); return false; } return true; } }); LimitedWipConfigurable.registerSettingsListener(ApplicationManager.getApplication(), this); } private void notifyThatCommitWasCancelled(Project project) { NotificationListener listener = new NotificationListener() { @Override public void hyperlinkUpdate(@NotNull Notification notification, @NotNull HyperlinkEvent event) { allowCommitOnceWithoutCheck = true; boolean succeeded = showCommitDialog(0); if (!succeeded) { allowCommitOnceWithoutCheck = false; } } }; Notification notification = new Notification( PluginId.displayName, "Commit was cancelled because change size is above threshold<br/>", "(<a href=\"\">Click here</a> to force commit anyway)", NotificationType.ERROR, listener ); project.getMessageBus().syncPublisher(Notifications.TOPIC).notify(notification); } /** * Use retrying logic because of UI deadlock (not sure why exactly this happened): * "AWT-EventQueue-1 14.0.2#IU-139.658.4, eap:true" prio=0 tid=0x0 nid=0x0 waiting on condition * java.lang.Thread.State: WAITING on com.intellij.util.concurrency.Semaphore$Sync@4071d2c1 * at sun.misc.Unsafe.park(Native Method) * at java.util.concurrent.locks.LockSupport.park(LockSupport.java:156) * at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:811) * at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireSharedInterruptibly(AbstractQueuedSynchronizer.java:969) * at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireSharedInterruptibly(AbstractQueuedSynchronizer.java:1281) * at com.intellij.util.concurrency.Semaphore.waitForUnsafe(Semaphore.java:74) * at com.intellij.openapi.util.ActionCallback.waitFor(ActionCallback.java:269) * at com.intellij.openapi.util.AsyncResult.getResultSync(AsyncResult.java:147) * at com.intellij.openapi.util.AsyncResult.getResultSync(AsyncResult.java:142) * at limitedwip.watchdog.components.DisableLargeCommitsAppComponent$2.hyperlinkUpdate(DisableLargeCommitsAppComponent.java:65) * at com.intellij.notification.impl.ui.NotificationsUtil$1.hyperlinkUpdate(NotificationsUtil.java:75) */ private static boolean showCommitDialog(int showCommitDialogAttempts) { if (showCommitDialogAttempts > maxShowCommitDialogAttempts) { return false; } DataContext dataContext = DataManager.getInstance().getDataContextFromFocus().getResultSync(500); if (dataContext == null) { return showCommitDialog(showCommitDialogAttempts + 1); } AnActionEvent actionEvent = new AnActionEvent( null, dataContext, ActionPlaces.UNKNOWN, new Presentation(), ActionManager.getInstance(), 0 ); new CommonCheckinProjectAction().actionPerformed(actionEvent); return true; } @Override public void disposeComponent() { } @NotNull @Override public String getComponentName() { return this.getClass().getCanonicalName(); } @Override public void onSettingsUpdate(LimitedWIPSettings settings) { this.enabled = settings.disableCommitsAboveThreshold; this.maxChangeSizeInLines = settings.maxLinesInChange; } }